﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIObjects.TreeViewInterfaces;
using System.Collections.ObjectModel;
using TreeBuilder.Contracts;
using MvvmTreeView;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
using AbstraX.ClientInterfaces;
using TreeBuilder.Services;

namespace TreeBuilder.ViewModels
{
    [TreeNode(typeof(TreeBuilderTreeNodeBase), "")]
    public abstract class TreeBuilderTreeNodeItemNode : TreeBuilderTreeNodeBase, ITreeNodeItemNode
    {
        protected TreeNodePropertyType currentPropertyTypes;
        protected TransferCreationMappingType currentMappingTypes;

        public TreeBuilderTreeNodeItemNode(ITreeBuilderService treeBuilderService, ITreeView treeView, ITreeNode parent) : base(treeBuilderService, treeView, parent)
        {
        }

        public abstract override AbstraX.ClientInterfaces.IBase BaseObject { get; set;  }
        public abstract override string ID { get; }
        public abstract override void LoadChildren();
        public abstract override IEnumerable<ITreeNodeBase> Children { get; }
        public abstract override string Text { get; set; }
        public abstract ITreeNodeBase Node { get; set; }
        public abstract void UpdatePropertyTypes();
        public abstract void UpdateMappingTypes();
        public abstract override ITreeNode ShallowClone();
        public abstract override ITreeNode DeepClone();
        public abstract override bool HasLayoverImage<T>(out T image);
        public abstract override void DragStarting(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e);
        public abstract override void DragCompleted(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e);
        public abstract override void ItemDropped(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e, int insertionIndex);
        public abstract override void GiveFeedback(object sender, UIObjects.DragDropInterfaces.GiveFeedbackEventArgs e);
        public abstract override void DragEnter(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract override void DragLeave(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract override void DragOver(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract override void OnSelect();

        #region Image Properties

        private List<IContextMenuItem> GetNodeImageURLPropertyBindingMenus()
        {
            List<IContextMenuItem> subMenus = new List<IContextMenuItem>();

            if (this.BaseObject is IElement)
            {
                IElement element = (IElement)this.BaseObject;

                foreach (var attribute in element.Attributes)
                {
                    if (attribute.DataType.Supports(typeof(string)))
                    {
                        var bindAttribute = attribute;

                        subMenus.Add(new ContextMenuItem(null, bindAttribute.Name, bindAttribute.Name, (sender, e) =>
                        {
                            AddNodePropertyBinding(TreeNodePropertyType.Image, bindAttribute, true);
                        }));
                    }
                }
            }

            subMenus.Add(new ContextMenuItem(null, "Query...", "CreateQueryImageValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.Image, TreeNodePropertyStockBinding.FromQuery);
            }));

            subMenus.Add(new ContextMenuItem(null, "Advanced Query...", "CreateQueryImageValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.Image, TreeNodePropertyStockBinding.FromQuery);
            }));

            return subMenus;
        }

        private List<IContextMenuItem> GetNodeImagePropertyBindingMenus()
        {
            List<IContextMenuItem> subMenus = new List<IContextMenuItem>();

            if (this.BaseObject is IElement)
            {
                IElement element = (IElement)this.BaseObject;

                foreach (var childElement in element.ChildElements)
                {
                    if (childElement.DataType.IsImageType)
                    {
                        var bindChildElement = childElement;

                        subMenus.Add(new ContextMenuItem(null, bindChildElement.Name, bindChildElement.Name, (sender, e) =>
                        {
                            AddNodePropertyBinding(TreeNodePropertyType.Text, bindChildElement);
                        }));
                    }
                }
            }

            subMenus.Add(new ContextMenuItem(null, "Query...", "CreateQueryImageValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.Image, TreeNodePropertyStockBinding.FromQuery);
            }));

            subMenus.Add(new ContextMenuItem(null, "Advanced Query...", "CreateQueryImageValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.Image, TreeNodePropertyStockBinding.FromQuery);
            }));

            return subMenus;
        }

        private List<IContextMenuItem> GetNodeImagePropertyOptionsMenus(PropertyTypeAttribute propertyTypeAttribute)
        {
            var menus = new List<IContextMenuItem>
            {
                new ContextMenuItem(null, "Upload New Image", "UploadNewImage", (sender, e) =>
                    {
                    }),
                new ContextMenuItem(null, "Add From Image Editor", "AddFromImageEditor", (sender, e) =>
                    {
                        NodeImageLoadOperation imageLoadOperation = treeBuilderService.LoadImageEditor(this.Node, this.BaseObject.Name + ".png");

                        if (imageLoadOperation.HasException)
                        {
                            var createOperation = treeBuilderService.CreatePropertyBindingNode(this.Node, TreeNodePropertyType.Image, TreeNodePropertyStockBinding.FromImageUpload);

                            createOperation.NodeCreated += (parent, e2) =>
                            {
                                var node = new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, createOperation.NewNode, this);

                                this.Nodes.Add(node);
                                base.Expand();

                                treeBuilderService.ReportException(node, imageLoadOperation.Exception);
                            };
                        }

                        imageLoadOperation.ExceptionOccured += (parent2, e3) =>
                        {
                            var createOperation = treeBuilderService.CreatePropertyBindingNode(this.Node, TreeNodePropertyType.Image, TreeNodePropertyStockBinding.FromImageUpload);

                            createOperation.NodeCreated += (parent, e2) =>
                            {
                                var node = new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, createOperation.NewNode, this);

                                this.Nodes.Add(node);
                                base.Expand();

                                treeBuilderService.ReportException(node, imageLoadOperation.Exception);
                            };
                        };

                        imageLoadOperation.ImageLoaded += (parent2, e3) =>
                        {
                            var createOperation = treeBuilderService.CreateImageBindingNode(this.Node, e3.TreeNodeImage);

                            createOperation.NodeCreated += (parent, e2) =>
                            {
                                var node = new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, createOperation.NewNode, this);

                                node.TreeNodeImage = e3.TreeNodeImage;

                                this.Nodes.Add(node);
                                base.Expand();
                            };
                        };
                    }),
                new ContextMenuItem(null, "Select Image Resource", "SelectImageResource", (sender, e) =>
                    {
                    }),
                new ContextMenuItem(null, "Create Placeholder", "CreatePlaceholderImageValue", (sender, e) =>
                    {
                    }),
                new ContextMenuItem()
                    {
                        Text = "Bind To",
                        Items = GetNodeImagePropertyBindingMenus()
                    },
                new ContextMenuItem()
                    {
                        Text = "Bind URL To",
                        Items = GetNodeImageURLPropertyBindingMenus()
                    }
            };

            return menus;
        }

        #endregion
        #region Children Properties

        private List<IContextMenuItem> GetNodeChildrenPropertyBindingMenus()
        {
            List<IContextMenuItem> subMenus = new List<IContextMenuItem>();

            if (this.BaseObject is IElement)
            {
                IElement element = (IElement)this.BaseObject;

                foreach (var childElement in element.ChildElements)
                {
                    if (childElement.DataType.IsCollectionType)
                    {
                        var bindElement = childElement;

                        subMenus.Add(new ContextMenuItem(null, bindElement.Name, bindElement.Name, (sender, e) =>
                        {
                            AddNodePropertyBinding(TreeNodePropertyType.Children, bindElement);
                        }));
                    }
                }
            }

            subMenus.Add(new ContextMenuItem(null, "Query...", "CreateQueryChildrenValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.Children, TreeNodePropertyStockBinding.FromQuery);
            }));

            subMenus.Add(new ContextMenuItem(null, "Advanced Query...", "CreateQueryChildrenValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.Children, TreeNodePropertyStockBinding.FromQuery);
            }));

            return subMenus;
        }

        private List<IContextMenuItem> GetNodeChildrenPropertyOptionsMenus(PropertyTypeAttribute propertyTypeAttribute)
        {
            var menus = new List<IContextMenuItem>
            {
                new ContextMenuItem(null, "Create Stub", "CreateStubChildrenValue", (sender, e) =>
                    {
                    AddNodePropertyBinding(TreeNodePropertyType.Children, TreeNodePropertyStockBinding.Stub);
                    }),
                new ContextMenuItem(null, "Create Placeholder", "CreatePlaceholderChildrenValue", (sender, e) =>
                    {
                    }),
                new ContextMenuItem()
                    {
                        Text = "Bind To",
                        Items = GetNodeChildrenPropertyBindingMenus()
                    }
            };

            return menus;
        }

        #endregion
        #region General Properties

        public void AddNodePropertyBinding(TreeNodePropertyType propertyType, IAttribute attribute, bool sourceIsURLBinding)
        {
            var createOperation = treeBuilderService.CreatePropertyBindingNode(this.Node, propertyType, attribute, sourceIsURLBinding);

            createOperation.NodeCreated += (parent, e) =>
            {
                this.Nodes.Add(new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                base.Expand();
            };
        }

        public void AddNodePropertyBinding(TreeNodePropertyType propertyType, IAttribute attribute)
        {
            var createOperation = treeBuilderService.CreatePropertyBindingNode(this.Node, propertyType, attribute);

            createOperation.NodeCreated += (parent, e) =>
            {
                this.Nodes.Add(new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                base.Expand();
            };
        }

        public void AddNodePropertyBinding(TreeNodePropertyType propertyType, IElement element)
        {
            var createOperation = treeBuilderService.CreatePropertyBindingNode(this.Node, propertyType, element);

            createOperation.NodeCreated += (parent, e) =>
            {
                this.Nodes.Add(new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                base.Expand();
            };
        }

        public void AddNodePropertyBinding(TreeNodePropertyType propertyType, TreeNodePropertyStockBinding stockBinding)
        {
            if (stockBinding == TreeNodePropertyStockBinding.FromQuery)
            {
                AddQuery(null, new RoutedEventArgs());
            }
            else if (stockBinding == TreeNodePropertyStockBinding.FromAdvancedQuery)
            {
                AddAdvancedQuery(null, new RoutedEventArgs());
            }
            else
            {
                var createOperation = treeBuilderService.CreatePropertyBindingNode(this.Node, propertyType, stockBinding);

                createOperation.NodeCreated += (parent, e) =>
                {
                    this.Nodes.Add(new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                    base.Expand();
                };
            }
        }

        private List<IContextMenuItem> GetNodePropertyBindingMenus(PropertyTypeAttribute propertyTypeAttribute, TreeNodePropertyType propertyType)
        {
            List<IContextMenuItem> subMenus = new List<IContextMenuItem>();

            if (this.BaseObject is IElement)
            {
                IElement element = (IElement)this.BaseObject;

                if (propertyTypeAttribute.IsTag)
                {
                    subMenus.Add(new ContextMenuItem(null, element.Name, element.Name, (sender, e) =>
                    {
                        AddNodePropertyBinding(propertyType, element);
                    }));
                }

                foreach (var attribute in element.Attributes)
                {
                    if (attribute.DataType.Supports(propertyTypeAttribute.PropertyType))
                    {
                        var bindAttribute = attribute;

                        subMenus.Add(new ContextMenuItem(null, bindAttribute.Name, bindAttribute.Name, (sender, e) =>
                        {
                            AddNodePropertyBinding(propertyType, bindAttribute);
                        }));
                    }
                }
            }

            subMenus.Add(new ContextMenuItem(null, "Query...", "CreateQueryAttributeValue", (sender, e) =>
            {
                AddNodePropertyBinding(propertyType, TreeNodePropertyStockBinding.FromQuery);
            }));

            subMenus.Add(new ContextMenuItem(null, "Advanced Query...", "CreateQueryAttributeValue", (sender, e) =>
            {
                AddNodePropertyBinding(propertyType, TreeNodePropertyStockBinding.FromQuery);
            }));

            return subMenus;
        }

        private List<IContextMenuItem> GetNodePropertyOptionsMenus(PropertyTypeAttribute propertyTypeAttribute, TreeNodePropertyType propertyType)
        {
            var menus = new List<IContextMenuItem>
            {
                new ContextMenuItem(null, "Create Hard Value", "CreateHardTextValue", (sender, e) =>
                    {
                        AddNodePropertyBinding(propertyType, TreeNodePropertyStockBinding.HardValue);
                    }),
                new ContextMenuItem(null, "Create Stub", "CreateStubTextValue", (sender, e) =>
                    {
                        AddNodePropertyBinding(propertyType, TreeNodePropertyStockBinding.Stub);
                    }),
                new ContextMenuItem(null, "Create Placeholder", "CreatePlaceholderTextValue", (sender, e) =>
                    {
                        AddNodePropertyBinding(propertyType, TreeNodePropertyStockBinding.Placeholder);
                    }),
                new ContextMenuItem()
                    {
                        Text = "Bind To",
                        Items = GetNodePropertyBindingMenus(propertyTypeAttribute, propertyType)
                    }
            };

            return menus;
        }

        #endregion
        #region Transfer/Creation Mappings

        private List<IContextMenuItem> GetTransferCreationOptionsMenus()
        {
            var menus = new List<IContextMenuItem>();

            foreach (var field in typeof(TransferCreationMappingType).GetFields())
            {
                if ((field.Attributes & System.Reflection.FieldAttributes.Static) > 0)
                {
                    var mappingAttribute = (TransferCreationMappingAttribute)field.GetCustomAttributes(typeof(TransferCreationMappingAttribute), true).Single();
                    var mappingType = (TransferCreationMappingType)field.GetValue(null);

                    if (((currentMappingTypes & mappingType) != mappingType) && !this.nodes.Any(n => ((n is TreeBuilderTreeNodeTransferMapping) && ((TreeBuilderTreeNodeTransferMapping)n).TransferCreationMappingType == mappingType)))
                    {
                        menus.Add(new ContextMenuItem(null, mappingAttribute.DisplayText, mappingAttribute.DisplayText, (sender, e) =>
                        {
                            var createOperation = treeBuilderService.CreateTransferCreationMappingNode(this.Node, mappingType, this.BaseObject);

                            createOperation.NodeCreated += (parent, e2) =>
                            {
                                this.Nodes.Add(new TreeBuilderTreeNodeTransferMapping(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                                base.Expand();
                            };
                        }));
                    }
                }
            }

            return menus;
        }

        #endregion
        #region Context Menus

        private List<IContextMenuItem> GetContextMenuPropertyOptionsMenus()
        {
            var menus = new List<IContextMenuItem>();

            menus.Add(new ContextMenuItem(null, "Create Top Level Context Menu", "CreateContextMenu", (sender, e) =>
            {
                var createOperation = treeBuilderService.CreateMenuItem(this.Node, this.BaseObject.Parent);

                createOperation.NodeCreated += (parent, e2) =>
                {
                    this.Nodes.Add(new TreeBuilderTreeNodeMenuItem(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                    base.Expand();
                };
            }));

            menus.Add(new ContextMenuItem(null, "Create Stub", "CreateStubMenuValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.ContextMenu, TreeNodePropertyStockBinding.Stub);
            }));
            menus.Add(new ContextMenuItem(null, "Create Placeholder", "CreatePlaceholderMenuValue", (sender, e) =>
            {
                AddNodePropertyBinding(TreeNodePropertyType.ContextMenu, TreeNodePropertyStockBinding.Placeholder);
            }));

            return menus;
        }

        #endregion

        private List<IContextMenuItem> GetNodePropertyMenus()
        {
            var menus = new List<IContextMenuItem>();

            foreach (var field in typeof(TreeNodePropertyType).GetFields())
            {
                if ((field.Attributes & System.Reflection.FieldAttributes.Static) > 0)
                {
                    var propertyTypeAttribute = (PropertyTypeAttribute)field.GetCustomAttributes(typeof(PropertyTypeAttribute), true).Single();
                    var propertyType = (TreeNodePropertyType)field.GetValue(null);

                    if (propertyType == TreeNodePropertyType.Image)
                    {
                        if (((currentPropertyTypes & TreeNodePropertyType.Image) != TreeNodePropertyType.Image) && !this.nodes.Any(n => ((n is TreeBuilderTreeNodePropertyBinding) && ((TreeBuilderTreeNodePropertyBinding)n).PropertyType == TreeNodePropertyType.Image)))
                        {
                            var contextMenuItem = new ContextMenuItem
                            {
                                Text = propertyTypeAttribute.DisplayText,
                                Items = GetNodeImagePropertyOptionsMenus(propertyTypeAttribute)
                            };

                            menus.Add(contextMenuItem);
                        }
                    }
                    else if (propertyType == TreeNodePropertyType.Children)
                    {
                        // no restrictions on number of possible children

                        var contextMenuItem = new ContextMenuItem
                        {
                            Text = propertyTypeAttribute.DisplayText,
                            Items = GetNodeChildrenPropertyOptionsMenus(propertyTypeAttribute)
                        };

                        menus.Add(contextMenuItem);
                    }
                    else if (propertyType == TreeNodePropertyType.ContextMenu)
                    {
                        if (((currentPropertyTypes & propertyType) != propertyType) && !this.nodes.Any(n => ((n is TreeBuilderTreeNodePropertyBinding) && ((TreeBuilderTreeNodePropertyBinding)n).PropertyType == propertyType)))
                        {
                            var contextMenuItem = new ContextMenuItem
                            {
                                Text = propertyTypeAttribute.DisplayText,
                                Items = GetContextMenuPropertyOptionsMenus()
                            };

                            menus.Add(contextMenuItem);
                        }
                    }
                    else
                    {
                        if (((currentPropertyTypes & propertyType) != propertyType) && !this.nodes.Any(n => ((n is TreeBuilderTreeNodePropertyBinding) && ((TreeBuilderTreeNodePropertyBinding)n).PropertyType == propertyType)))
                        {
                            var contextMenuItem = new ContextMenuItem
                            {
                                Text = propertyTypeAttribute.DisplayText,
                                Items = GetNodePropertyOptionsMenus(propertyTypeAttribute, propertyType)
                            };

                            menus.Add(contextMenuItem);
                        }
                    }
                }
            }

            return menus;
        }

        public override ContextMenu ContextMenu
        {
            get
            {
                contextMenu = new ContextMenu
                {
                    Items = new List<IContextMenuItem>()
                    {
                        new ContextMenuItem
                        {
                            Text = "Add Node Property",
                            Items = GetNodePropertyMenus(),
                        },
                        new ContextMenuItem
                        {
                            Text = "Add Transfer/Creation Mappings",
                            Items = GetTransferCreationOptionsMenus(),
                        }

                    }
                };

                return contextMenu;
            }

            set
            {
                throw new NotImplementedException();
            }
        }

        public override bool? HasChildren
        {
            get { throw new NotImplementedException(); }
        }

        public override void Remove()
        {
            throw new NotImplementedException();
        }

        public override bool AllowDrop
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override bool AllowRename
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void AddNew(string shortCut)
        {
            throw new NotImplementedException();
        }

        public override ITreeNodeBase InternalNodeObject
        {
            get { throw new NotImplementedException(); }
        }
    }
}
